﻿using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.IO;
using System.Xml;
using AbstraX.Contracts;
using System.Text;
using Metaspec;
using System.Text.RegularExpressions;

namespace VisualStudioProvider
{
    [DebuggerDisplay("{Name}, {Include}, {ItemType}")]
    public class VSProjectItem : IVSProjectItem
    {
        static readonly Type s_ProjectItemElement;
        static readonly PropertyInfo s_ProjectItemElement_ItemType;
        static readonly PropertyInfo s_ProjectItemElement_Include;

        private VSProject project;
        private object internalProjectItem;
        private string pathName;
        private string itemType;
        private ICsFile iCSFile;

        public IProjectRoot ProjectRoot { get; set; }
        public IArchitectureLayer ArchitectureLayer { get; set; }

        static VSProjectItem()
        {
            s_ProjectItemElement = Type.GetType("Microsoft.Build.Construction.ProjectItemElement, Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false, false);

            s_ProjectItemElement_ItemType = s_ProjectItemElement.GetProperty("ItemType", BindingFlags.Public | BindingFlags.Instance);
            s_ProjectItemElement_Include = s_ProjectItemElement.GetProperty("Include", BindingFlags.Public | BindingFlags.Instance);
        }

        public object InternalProjectItem
        {
            get 
            { 
                return internalProjectItem; 
            }
        }

        public ICsFile ICsFile
        {
            get
            {
                return iCSFile;
            }
        }

        public string ItemType
        { 
            get
            {
                return itemType;
            }
        }

        public string Include { get; private set; }

        public VSProjectItem(VSProject project, object internalProjectItem)
        {
            this.itemType = s_ProjectItemElement_ItemType.GetValue(internalProjectItem, null) as string;
            this.Include = s_ProjectItemElement_Include.GetValue(internalProjectItem, null) as string;
            this.project = project;
            this.internalProjectItem = internalProjectItem;
            var file = new FileInfo(project.FileName);
            //var regex = new Regex(@"^(\w+(\.?\w+)+))\s*,\s*(?<assembly>[\w\.]+)(,\s?Version=(?<version>\d+\.\d+\.\d+\.\d+))?(,\s?Culture=(?<culture>\w+))?(,\s?PublicKeyToken=(?<token>\w+))?$");

            if (this.itemType == "WebReferenceUrl" || this.itemType == "Reference")
            {
                pathName = this.Include;
            }
            else
            {
                pathName = Path.Combine(file.DirectoryName, this.Include);
            }

            // todo - expand this

            if (this.ItemType == "Compile" || this.ItemType == "EntityDeploy")
            {
                if (project.IProject != null)
                {
                    iCSFile = ((ICsProject)project.IProject).getFiles().FirstOrDefault(f => f.getPath() == pathName);
                }
            }
        }

        public byte[] FileContents
        {
            get 
            {
                return File.ReadAllBytes(pathName);
            }
        }

        public string Name
        {
            get 
            {
                if (pathName != null)
                {
                    if (Uri.IsWellFormedUriString(pathName, UriKind.RelativeOrAbsolute))
                    {
                        pathName = this.Include;

                        return pathName;
                    }
                    else
                    {
                        var file = new FileInfo(pathName);

                        if (file.Exists)
                        {
                            return file.Name;
                        }
                        else
                        {
                            var directory = new DirectoryInfo(pathName);

                            if (directory.Exists)
                            {
                                return directory.Name;
                            }
                            else
                            {
                                return this.Include;
                            }
                        }
                    }
                }
                else
                {
                    return this.Include;
                }
            }
        }

        public T GetFileContents<T>()
        {
            switch (typeof(T).Name)
            {
                case "String":

                    var fileContents = this.FileContents;
                    var bom = CodePoint.GetBOM(fileContents);
                    var contents = ASCIIEncoding.ASCII.GetString(fileContents, bom.Length, fileContents.Length - bom.Length);

                    return (T) (object) contents;

                default:
                    throw new Exception("Unsupported content type");
            }
        }
    }
}
